﻿#region

using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows.Forms;

#endregion

namespace Microsoft.Win32.DesktopWindowManager
{
  /// <summary>
  ///   Main DWM class, provides glass sheet effect and blur behind.
  /// </summary>
  [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
  public static class DesktopWindowManager
  {
    private static readonly object ColorizationColorChangedKey = new object();
    private static readonly object CompositionChangedKey = new object();
    private static EventHandlerList eventHandlerList;
    private static readonly object NonClientRenderingChangedKey = new object();
    //static object WindowMaximizedChangedKey = new object();
    private static readonly object[] keys = new[]
                                              {
                                                CompositionChangedKey, NonClientRenderingChangedKey,
                                                ColorizationColorChangedKey /*, WindowMaximizedChangedKey*/
                                              };

    private static readonly object _lock = new object();
    private static MessageWindow _window;

    /// <summary>
    ///   Occurs when the colorization color has changed.
    /// </summary>
    public static event EventHandler ColorizationColorChanged
    {
      add { AddEventHandler(ColorizationColorChangedKey, value); }
      remove { RemoveEventHandler(ColorizationColorChangedKey, value); }
    }

    /// <summary>
    ///   Occurs when the desktop window composition has been enabled or disabled.
    /// </summary>
    public static event EventHandler CompositionChanged
    {
      add { AddEventHandler(CompositionChangedKey, value); }
      remove { RemoveEventHandler(CompositionChangedKey, value); }
    }

    /// <summary>
    ///   Occurs when the non-client area rendering policy has changed.
    /// </summary>
    public static event EventHandler NonClientRenderingChanged
    {
      add { AddEventHandler(NonClientRenderingChangedKey, value); }
      remove { RemoveEventHandler(NonClientRenderingChangedKey, value); }
    }

    /*/// <summary>
		/// Occurs when a Desktop Window Manager (DWM) composed window is maximized.
		/// </summary>
		public static event EventHandler WindowMaximizedChanged
		{
			add { AddEventHandler(WindowMaximizedChangedKey, value); }
			remove { RemoveEventHandler(WindowMaximizedChangedKey, value); }
		}*/

    /// <summary>
    ///   Enable the Aero "Blur Behind" effect on the whole client area. Background must be black.
    /// </summary>
    /// <param name = "window">The window.</param>
    /// <param name = "enabled"><c>true</c> to enable blur behind for this window, <c>false</c> to disable it.</param>
    private static void AddEventHandler(object id, EventHandler value)
    {
      lock (_lock)
      {
        if (_window == null)
          _window = new MessageWindow();
        if (eventHandlerList == null)
          eventHandlerList = new EventHandlerList();
        eventHandlerList.AddHandler(id, value);
      }
    }

    private static void RemoveEventHandler(object id, EventHandler value)
    {
      lock (_lock)
      {
        if (eventHandlerList != null)
        {
          eventHandlerList.RemoveHandler(id, value);
        }
      }
    }

    #region Nested type: BlurBehind

    [StructLayout(LayoutKind.Sequential)]
    private struct BlurBehind
    {
      private BlurBehindFlags dwFlags;
      private readonly int fEnable;
      private IntPtr hRgnBlur;
      private int fTransitionOnMaximized;

      public BlurBehind(bool enabled)
      {
        fEnable = enabled ? 1 : 0;
        hRgnBlur = IntPtr.Zero;
        fTransitionOnMaximized = 0;
        dwFlags = BlurBehindFlags.Enable;
      }

      public Region Region
      {
        get { return Region.FromHrgn(hRgnBlur); }
      }

      public bool TransitionOnMaximized
      {
        get { return fTransitionOnMaximized > 0; }
        set
        {
          fTransitionOnMaximized = value ? 1 : 0;
          dwFlags |= BlurBehindFlags.TransitionOnMaximized;
        }
      }

      public void SetRegion(Graphics graphics, Region region)
      {
        hRgnBlur = region.GetHrgn(graphics);
        dwFlags |= BlurBehindFlags.BlurRegion;
      }
    }

    #endregion

    #region Nested type: BlurBehindFlags

    private enum BlurBehindFlags
    {
      Enable = 0x00000001,
      BlurRegion = 0x00000002,
      TransitionOnMaximized = 0x00000004
    }

    #endregion

    #region Nested type: MessageWindow

    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    private class MessageWindow : NativeWindow, IDisposable
    {
      private const int WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320;
      private const int WM_DWMCOMPOSITIONCHANGED = 0x031E;
      private const int WM_DWMNCRENDERINGCHANGED = 0x031F;
      //const int WM_DWMWINDOWMAXIMIZEDCHANGE = 0x0321;

      public MessageWindow()
      {
        var cp = new CreateParams {Style = 0, ExStyle = 0, ClassStyle = 0, Parent = IntPtr.Zero};
        cp.Caption = base.GetType().Name;
        CreateHandle(cp);
      }

      #region IDisposable Members

      public void Dispose()
      {
        DestroyHandle();
      }

      #endregion

      [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
      protected override void WndProc(ref Message m)
      {
        if (m.Msg >= WM_DWMCOMPOSITIONCHANGED && m.Msg <= WM_DWMCOLORIZATIONCOLORCHANGED)
          ExecuteEvents(m.Msg - WM_DWMCOMPOSITIONCHANGED);

        base.WndProc(ref m);
      }

      private static void ExecuteEvents(int idx)
      {
        if (eventHandlerList != null)
        {
          lock (_lock)
          {
            try
            {
              ((EventHandler) eventHandlerList[keys[idx]]).Invoke(null, EventArgs.Empty);
            }
            catch
            {
            }
            ;
          }
        }
      }
    }

    #endregion
  }
}